package com.zxx.coolweather._wuguan2.async;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import androidx.annotation.NonNull;

/**
 * 自定义异步任务框架
 *
 * @author zxx on 2022/1/12
 */
public abstract class MultiAsynctask<Param, Update, Result> {

    private static final int WHAT_UPDATE = 0x01;    //更新的what
    private static final int WHAT_RESULT = 0x02;    //发送结果的what

    private static final int DEFAULT_POOL_SIZE = 5; // 默认并发大小
    private static ExecutorService executorService; //默认的线程池
    private static Handler sHandler;                //发送结果的Handler

    private static Object HANDLER_LOCK = new Object();  //Handler的锁
    private ExecutorService mExecutorService;       //本地异步任务的执行器

    public MultiAsynctask() {
        this(getDufaultExecutor());
    }

    public MultiAsynctask(ExecutorService mExecutorService) {
        this.mExecutorService = mExecutorService;
    }

    //拿到默认的线程池
    private static ExecutorService getDufaultExecutor() {
        synchronized (MultiAsynctask.class) {
            if (executorService == null) {
                executorService = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
            }
            return executorService;
        }
    }

    //设置默认的线程池
    public static void setDefaultExecutor(ExecutorService executorService) {
        synchronized (MultiAsynctask.class) {
            MultiAsynctask.executorService = executorService;
        }
    }

    public static Handler getDefaultPoster() {
        synchronized (HANDLER_LOCK) {
            if (sHandler == null) {
                sHandler = new Poster();
            }
            return sHandler;
        }
    }

    //开始执行任务
    public final void execute(Param... parms) {
        mExecutorService.execute(new Tasker(parms));
    }

    protected abstract Result onExecuteTask(Param... parms);

    //发送进度更新到主线程
    public final void onPostUpdate(Update update) {
        Message.obtain();
        Message message = getDefaultPoster().obtainMessage();
        message.what = WHAT_UPDATE;
        message.obj = new Messager<Param, Update, Result>(this, update, null);
        message.sendToTarget();
    }

    //当返回进度更新的时候
    protected void onUpdate(Update update) {

    }

    //发送执行结果到主线程
    public final void onPostResult(Result result) {
        Message.obtain();
        Message message = getDefaultPoster().obtainMessage();
        message.what = WHAT_RESULT;
        message.obj = new Messager<>(this, null, result);
        message.sendToTarget();
    }

    //当返回执行结果的时候
    protected void onResult(Result result) {

    }


    private static class Messager<Param, Update, Result> {

        private final MultiAsynctask<Param, Update, Result> asynctask;
        private final Update update;
        private final Result result;

        public Messager(MultiAsynctask<Param, Update, Result> asynctask, Update update, Result result) {
            this.asynctask = asynctask;
            this.update = update;
            this.result = result;
        }

        //调用当前MultiAsynctask的主线程更新方法
        public void onUpdate() {
            asynctask.onUpdate(update);
        }

        //调用当前MultiAsynctask的主线程结果方法
        public void onResult() {
            asynctask.onResult(result);
        }

    }


    /**
     * 线程间通信使者
     */
    private static class Poster extends Handler {
        public Poster() {
            super(Looper.getMainLooper());
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            Messager<?, ?, ?> messager = (Messager<?, ?, ?>) msg.obj;
            if (msg.what == WHAT_RESULT) {
                messager.onResult();
            } else if (msg.what == WHAT_UPDATE) {
                messager.onUpdate();
            }
        }
    }

    /**
     * 任务执行器
     */
    private class Tasker implements Runnable {
        private Param[] params;

        public Tasker(Param... params) {
            this.params = params;
        }

        @Override
        public void run() {
            Result result = onExecuteTask(params);
            onPostResult(result);
        }
    }

}
